// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: LinuxUtil.scala 78 2010-05-13 12:25:41Z normen.mueller $
package scaup.io.jna

import java.io._
import com.sun.jna.Memory;
import scaup.io.OS
import scaup.io.FileType
import scaup.Views._

/**
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.4
 */
private[jna] object LinuxUtil {  
  private val mySharedMemory = try { new Memory(1024) } catch { case _ => null }
  
  private lazy val fileModeOffset =
    if (OS.isLinux && OS.is64Bit) 24
    else if (OS.isLinux && OS.is32Bit) 16
    else if (OS.isOSX) 8
    else 16
    
  private lazy val fileUserIDOffset =
    if (OS.isLinux && OS.is64Bit) fileModeOffset + 4
    else if (OS.isLinux && OS.is32Bit) fileModeOffset + 8
    else if (OS.isOSX) fileModeOffset + 4
    else fileModeOffset + 8
    
  private lazy val fileGroupIDOffset = fileUserIDOffset + 4  
  
  def getFileType(file: File): FileType.Value =
    if(file != null && mySharedMemory != null) {
      var path = file.absolutePath
      if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
      val cLibrary = JNALibraryLoader.cLibrary
      if(cLibrary != null)
        mySharedMemory synchronized {
          mySharedMemory.clear()
          val rc = cLibrary synchronized { 
            if(OS.isOSX) cLibrary.lstat(path, mySharedMemory)
            else  cLibrary.__lxstat64(0, path, mySharedMemory)
          }
          if (rc < 0)
            if (file.exists() || file.isDirectory() || file.isFile()) FileType.UNKNOWN // null
            else FileType.NONE
          val mode: Int =
            if(OS.isOSX) mySharedMemory.getShort(fileModeOffset)
            else mySharedMemory.getInt(fileModeOffset)
          val t = mode & 0170000
          if(t == 0120000) FileType.SYMLINK
          else if (t == 0040000) FileType.DIRECTORY
          else if (t == 0100000) FileType.FILE
          else
            if(file.exists() || file.isDirectory() || file.isFile()) FileType.UNKNOWN // null
            else FileType.NONE
        } else FileType.UNKNOWN // null
    } else FileType.UNKNOWN // null
  
  def getLinkTarget(file: File): Option[String] =
    if(file != null && mySharedMemory != null) {
      var path = file.absolutePath
      if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
      val cLibrary = JNALibraryLoader.cLibrary
      if(cLibrary != null)
        mySharedMemory synchronized {
          mySharedMemory.clear()
          val rc = cLibrary synchronized { cLibrary.readlink(path, mySharedMemory, 1024) }
          if(rc > 0) {
            val buffer = new Array[Byte](rc)
            mySharedMemory.read(0, buffer, 0, rc)
            Some(new String(buffer, 0, rc))
          } else None
        } else None    
    } else None
  
  /* ln -s <linkName> <link>
   * 
   * Terminology:
   * 
   * @locutor -> vc/svn/kwarc.info/locutor
   * \------/    \-----------------------/
   *   link              linkName
   */
  def createSymlink(link: Option[File], linkName: String): Option[File] = link match {
    case Some(link) =>
      if(link != null && linkName != null && mySharedMemory != null) {
        var path = link.absolutePath
        if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
        val cLibrary = JNALibraryLoader.cLibrary
        if(cLibrary != null) cLibrary synchronized { if(cLibrary.symlink(linkName, path) < 0) None else Some(link) }
        else None
      } else None
    case _ => None
  }
  
  /* **************************************************************************************************************** *\
   * The following functions don't reliable work on each platform.  Seems to be founded in JNA itself!! 
  \* **************************************************************************************************************** */
  
  def isExecutable(file: File): Boolean =
    if(file != null && mySharedMemory != null) {
      var path = file.absolutePath
      if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
      val cLibrary = JNALibraryLoader.cLibrary
      if(cLibrary != null) {
        mySharedMemory synchronized {
          mySharedMemory.clear();
          val rc = cLibrary synchronized {
            if(OS.isOSX) cLibrary.lstat(path, mySharedMemory)
            else cLibrary.__lxstat64(0, path, mySharedMemory)
          }
          if(rc >= 0) {
            var mode: Int = mySharedMemory.getInt(fileModeOffset)
            var fuid: Int = mySharedMemory.getInt(fileUserIDOffset)
            var fgid: Int = mySharedMemory.getInt(fileGroupIDOffset)
            var mask = 0111
            if(JNALibraryLoader.uid == fuid) mask = 0100 // check user
            else if (JNALibraryLoader.gid == fgid) mask = 0010 // check group 
            else mask = 0001 // check other.
            ((mode & 0777) & mask) != 0
          } else false                
        }
      } else false            
    } else false
  
  def setExecutable(file: File, set: Boolean): Boolean =
    if(file != null && mySharedMemory != null) {
      var path = file.absolutePath
      if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
      val cLibrary = JNALibraryLoader.cLibrary
      if(cLibrary != null) {
        mySharedMemory synchronized {
          mySharedMemory.clear()
          val rc = cLibrary synchronized {
            if(OS.isOSX) cLibrary.lstat(path, mySharedMemory)
            else cLibrary.__lxstat64(0, path, mySharedMemory)
          }
          if(rc >= 0) {
            val access = mySharedMemory.getInt(fileModeOffset) & 0777
            var mask = 0
            if((access & 0400) != 0) mask |= 0100
            if((access & 0040) != 0) mask |= 0010
            if((access & 0004) != 0) mask |= 0001
            if(mask != 0) cLibrary synchronized { if(cLibrary.chmod(path, if(set) mask | access else mask ^ access) < 0) false else true }
            else false
          } else false
        }
      } else false
    } else false
  
  def setWritable(file: File, set: Boolean): Boolean =
    if(file != null && mySharedMemory != null) {
      if(set) {
        var path = file.absolutePath
        if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
        val cLibrary = JNALibraryLoader.cLibrary
        if(cLibrary != null) {
          mySharedMemory synchronized {
            mySharedMemory.clear()
            val rc = cLibrary synchronized {
              if(OS.isOSX) cLibrary.lstat(path, mySharedMemory)
              else cLibrary.__lxstat64(0, path, mySharedMemory)
            }
            if(rc >= 0) {
              val access = mySharedMemory.getInt(fileModeOffset) & 0777
              var mask = 0
              if((access & 0400) != 0) mask |= 0200
              if((access & 0040) != 0) mask |= 0020
              if((access & 0004) != 0) mask |= 0002
              if(mask != 0) cLibrary synchronized { if(cLibrary.chmod(path, mask | access) < 0) false else true }
              else false
            } else false
          }
        } else false
      } else file.setReadOnly
    } else false
  
  def setSGID(file: File): Boolean =
    if(file != null && mySharedMemory != null) {
      var path = file.absolutePath
      if(path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
      val cLibrary = JNALibraryLoader.cLibrary
      if(cLibrary != null) {
        mySharedMemory synchronized {
          mySharedMemory.clear()
          val rc = cLibrary synchronized {
            if(OS.isOSX) cLibrary.stat(path, mySharedMemory)
            else cLibrary.__xstat64(0, path, mySharedMemory)
          }
          if (rc >= 0) {
            val access = mySharedMemory.getInt(fileModeOffset) & 07777
            var mask = 02000
            if((access & mask) == 0) cLibrary synchronized { if(cLibrary.chmod(path, mask | access) < 0) false else true }
            else false
          } else false
        }
      } else false            
    } else false  
}
